package com.molichuxing.framework.utils;

import java.io.Closeable;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.net.ssl.SSLContext;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * http请求工具类
 * 
 * @author zoumingyu
 * @date 2019年8月21日
 *
 */
public class HttpClientUtil {
	private static Logger logger = LoggerFactory.getLogger(HttpClientUtil.class); // 日志记录

	// 连接上一个url，获取response的返回等待时间
	public static final int SOCKET_TIMEOUT = 20 * 1000;
	// 连接一个url的连接等待时间
	public static final int CONNECT_TIMEOUT = 10 * 1000;
	// 编码格式
	private static final String CHARSET = "UTF-8";

	private static RequestConfig requestConfig;

	static {
		// 设置请求和传输超时时间
		requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT)
				.build();
	}

	/**
	 * get请求
	 * 
	 * @param url
	 *            url
	 * @param paramMap
	 *            请求参数
	 * @return
	 * @throws JSONException
	 */
	public static String doGet(String url, Map<String, String> paramMap) throws IOException, JSONException {
		return doGet(url, null, paramMap, CHARSET);
	}

	/**
	 * post请求
	 * 
	 * @param url
	 *            url
	 * @param paramMap
	 *            请求参数
	 * @return
	 * @throws JSONException
	 */
	public static String doPost(String url, Map<String, String> paramMap) throws IOException, JSONException {
		return doPost(url, null, paramMap, CHARSET);
	}

	/**
	 * get请求
	 * 
	 * @param url
	 *            url
	 * @param headerMap
	 *            header参数
	 * @param paramMap
	 *            请求参数
	 * @return
	 * @throws IOException
	 * @throws JSONException
	 */
	public static String doGet(String url, Map<String, String> headerMap, Map<String, String> paramMap)
			throws IOException, JSONException {
		return doGet(url, headerMap, paramMap, CHARSET);
	}

	/**
	 * post请求
	 * 
	 * @param url
	 *            url
	 * @param headerMap
	 *            header参数
	 * @param paramMap
	 *            请求参数
	 * @return
	 * @throws IOException
	 * @throws JSONException
	 */
	public static String doPost(String url, Map<String, String> headerMap, Map<String, String> paramMap)
			throws IOException, JSONException {
		return doPost(url, headerMap, paramMap, CHARSET);
	}

	/**
	 * get请求
	 * 
	 * @param url
	 *            url
	 * @param headerMap
	 *            header参数
	 * @param paramMap
	 *            请求参数
	 * @param charset
	 *            编码
	 * @return
	 * @throws IOException
	 * @throws JSONException
	 */
	public static String doGet(String url, Map<String, String> headerMap, Map<String, String> paramMap, String charset)
			throws IOException, JSONException {
		return send(url, HttpMethods.GET, headerMap, paramMap, charset);
	}

	/**
	 * post请求
	 * 
	 * @param url
	 *            url
	 * @param headerMap
	 *            header参数
	 * @param paramMap
	 *            请求参数
	 * @param charset
	 *            编码
	 * @return
	 * @throws IOException
	 * @throws JSONException
	 */
	public static String doPost(String url, Map<String, String> headerMap, Map<String, String> paramMap,
			String charset) throws IOException, JSONException {
		return send(url, HttpMethods.POST, headerMap, paramMap, charset);
	}

	/**
	 * http请求
	 * 
	 * @param url
	 *            url
	 * @param httpMethod
	 *            请求方式
	 * @param headerMap
	 *            header参数
	 * @param paramMap
	 *            请求参数
	 * @param charset
	 *            编码
	 * @return
	 * @throws IOException
	 * @throws JSONException
	 */
	public static String send(String url, HttpMethods httpMethod, Map<String, String> headerMap,
			Map<String, String> paramMap, String charset) throws IOException, JSONException {
		String result = null;
		HttpRequestBase httpRequest = null;
		if (HttpMethods.GET.equals(httpMethod)) {
			// 拼接请求参数
			List<NameValuePair> pairs = null;
			if (paramMap != null && !paramMap.isEmpty()) {
				pairs = new ArrayList<NameValuePair>(paramMap.size());
				for (String key : paramMap.keySet()) {
					String value = paramMap.get(key);
					if (StringUtils.isNotBlank(value)) {
						pairs.add(new BasicNameValuePair(key, value));
					}
				}
			}
			// 创建请求对象
			httpRequest = getHttpRequest(url, httpMethod, pairs, charset);
		} else if (HttpMethods.POST.equals(httpMethod)) {
			// 拼接请求参数
			JSONObject paramsObject = null;
			if (paramMap != null && !paramMap.isEmpty()) {
				paramsObject = new JSONObject();
				for (String key : paramMap.keySet()) {
					String value = paramMap.get(key);
					if (StringUtils.isNotBlank(value)) {
						paramsObject.put(key, value);
					}
				}
			}
			// 创建请求对象
			httpRequest = getHttpRequest(url, httpMethod, paramsObject, charset);
		}

		// 设置header信息
		if (headerMap != null && !headerMap.isEmpty()) {
			for (String key : headerMap.keySet()) {
				httpRequest.setHeader(key, headerMap.get(key));
			}
		}
		HttpResponse response = null;
		try {
			// 创建httpClient
			CloseableHttpClient httpClient = getHttpClient(url);
			response = httpClient.execute(httpRequest);
			int statusCode = response.getStatusLine().getStatusCode();
			if (statusCode != HttpStatus.SC_OK) {
				httpRequest.abort();
				logger.error("HTTP " + httpMethod + "请求失败(error code :" + statusCode + ")");
			}
			HttpEntity entity = response.getEntity();
			if (entity != null) {
				result = EntityUtils.toString(entity, charset == null ? CHARSET : charset);
			}
			EntityUtils.consume(entity);
		} finally {
			if (response != null && response instanceof Closeable) {
				((Closeable) response).close();
			}
		}

		return result;
	}

	/**
	 * http请求
	 * 
	 * @param url
	 *            url
	 * @param httpMethod
	 *            请求方式
	 * @param headerMap
	 *            header参数
	 * @param params
	 *            请求参数(例："{ \"key\": \"text\", \"key\": object, ...}")
	 * @param charset
	 *            编码
	 * @return
	 * @throws IOException
	 * @throws JSONException
	 */
	public static String send(String url, HttpMethods httpMethod, Map<String, String> headerMap, String params,
			String charset) throws IOException, JSONException {
		String result = null;
		// 创建请求对象
		HttpRequestBase httpRequest = getHttpRequest(url, httpMethod, params, charset);
		// 设置header信息
		if (headerMap != null && !headerMap.isEmpty()) {
			for (String key : headerMap.keySet()) {
				httpRequest.setHeader(key, headerMap.get(key));
			}
		}
		HttpResponse response = null;
		try {
			// 创建httpClient
			CloseableHttpClient httpClient = getHttpClient(url);
			response = httpClient.execute(httpRequest);
			int statusCode = response.getStatusLine().getStatusCode();
			if (statusCode != HttpStatus.SC_OK) {
				httpRequest.abort();
				logger.error("HTTP " + httpMethod + "请求失败(error code :" + statusCode + ")");
			}
			HttpEntity entity = response.getEntity();
			if (entity != null) {
				result = EntityUtils.toString(entity, charset == null ? CHARSET : charset);
			}
			EntityUtils.consume(entity);
		} finally {
			if (response != null && response instanceof Closeable) {
				((Closeable) response).close();
			}
		}

		return result;
	}

	private static HttpRequestBase getHttpRequest(String url, HttpMethods httpMethod, JSONObject paramsObject,
			String charset) throws IOException, JSONException {
		return getHttpRequest(url, httpMethod, paramsObject.toString(), charset);
	}

	private static HttpRequestBase getHttpRequest(String url, HttpMethods httpMethod, String params, String charset)
			throws IOException, JSONException {
		return getHttpRequest(url, httpMethod, new StringEntity(params, charset == null ? CHARSET : charset), charset);
	}


	@SuppressWarnings("unused")
	private static HttpRequestBase getHttpRequest(String url, HttpMethods httpMethod, List<NameValuePair> pairs,
			String charset) throws IOException, JSONException {
		return getHttpRequest(url, httpMethod, pairs != null ? new UrlEncodedFormEntity(pairs, charset == null ? CHARSET : charset) : null,
				charset);
	}

	private static HttpRequestBase getHttpRequest(String url, HttpMethods httpMethod, StringEntity se, String charset)
			throws IOException, JSONException {
		HttpRequestBase httpRequest = null;
		switch (httpMethod) {
		// HttpGet
		case GET:
			if (se != null) {
				url += "?" + EntityUtils.toString(se);
			}
			httpRequest = new HttpGet(url);
			break;
		// HttpPost
		case POST:
			HttpPost httpPost = new HttpPost(url);
			if (se != null) {
				httpPost.setEntity(se);
			}
			httpRequest = httpPost;
			break;
		default:
			break;
		}

		return httpRequest;
	}

	/**
	 * TODO 获取httpclient
	 * 
	 * @return
	 */
	public static CloseableHttpClient getHttpClient(String url) {
		// 判断是否是https请求
		if (url.startsWith("https://")) {
			// sslClient(httpClient, null, null);
		}
		return HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).build();
	}

	/**
	 * TODO 获取初始化SSLContext
	 * 
	 * @return
	 */
	@SuppressWarnings("unused")
	private static SSLContext createSSLContext() {
		SSLContext sslcontext = null;
		try {
			sslcontext = SSLContext.getInstance("TLS");
//			sslcontext.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new SecureRandom());
		} catch (NoSuchAlgorithmException e) {
			logger.error("createSSLContext异常", e);
		}
		return sslcontext;
	}

	public enum HttpMethods {
		/**
		 * 求获取Request-URI所标识的资源
		 */
		GET(1, "GET"),

		/**
		 * 向指定资源提交数据进行处理请求（例如提交表单或者上传文件）。数据被包含在请求体中。
		 * POST请求可能会导致新的资源的建立和/或已有资源的修改
		 */
		POST(2, "POST");
		private int code;
		private String name;

		private HttpMethods(int code, String name) {
			this.code = code;
			this.name = name;
		}

		public String getName() {
			return name;
		}

		public int getCode() {
			return code;
		}
	}

//	public static void main(String[] args) throws IOException, JSONException {
//		Map<String, String> map = new HashMap<>();
//		map.put("sss", "ssss");
//		map.put("vvv", "vvvv");
//		map.put("ccc", "cccc");
//		HttpClientUtil.doPost("", map);
//	}
}
